use crate::chunker::StatefulChunker;
use std::io::{Read, Result as IoResult};

/// Chunking data from an [Read].
///
/// `M` is the buffer size.
pub struct SyncChunk<const M: usize, R: Read, C: StatefulChunker> {
    src: R,
    chunker: C,
    buf: Box<[u8; M]>,

    start: usize,
    end: usize,
    input_ended: bool,
}

impl<const M: usize, R: Read, C: StatefulChunker> SyncChunk<M, R, C> {
    pub fn new(src: R, chunker: C) -> Self {
        Self {
            src,
            chunker,
            buf: Box::new([0u8; M]),
            start: 0,
            end: 0,
            input_ended: false,
        }
    }

    pub fn chunk_next(&mut self) -> IoResult<Option<(usize, C::Output)>> {
        loop {
            if self.end - self.start == 0 {
                if self.input_ended {
                    return Ok(None);
                }
                let mut filled = 0;
                while filled < M {
                    let n = self.src.read(&mut self.buf[filled..])?;
                    if n == 0 {
                        self.input_ended = true;
                        break;
                    }
                    filled += n;
                }
                self.start = 0;
                self.end = filled;
            }
            if let Some((l, out)) = self.chunker.resume_on(&self.buf[self.start..self.end]) {
                self.start += l;
                return Ok(Some(out));
            } else {
                self.start = self.end;
                if self.input_ended {
                    return Ok(Some(self.chunker.drain()));
                }
            }
        }
    }
}
